package GameLogic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.lwjgl.opengl.Display;

import NGLR.NGLR;
import Renderer.*;
import Input.BasicInput;
import LevelParser.levelParser; 

public class Logic
{
	private BasicInput in;
	private int a;
	private double tmp = 0;
    private double tmp2 = 0;
    private float tmp3 =0;
    private int idModelToki;
    private int idModelElf;
    private int idModelAxe;
    private int idModelMage;
    private int idModelMagic;
    private int idModelOwl;
    private int[] idModelKills;
    private int[] idModelStatics;
    private int[] idTokiCurse;
    private int idModelSplatter;
    private double staticSpawnRate;
    private double lastStatic;
	private Background bground;
	HashSet<Entity> activeEntities;
	HashSet<EntityProjectile> activeProjectiles;
	HashSet<Effect> Effects;
	String[] SpawnableEntityID;
	String[][] Spawnlist;
	EntityPlayer player;
	protected double offset;
	public int currentLevel;
	public ArrayList<EntityCoordinate> renderable;
	
    public NGLR glRenderer;
    public levelParser parser;
    
    private boolean isLevelLoaded;

    private RendererSettings windowSettings;
    private ProjectionSettings projectionSettings;
    private Set s;
    private Set e;
	
	Logic(){
        windowSettings = new RendererSettings();
        projectionSettings = new ProjectionSettings();
        windowSettings.FpsCap = 60;
        windowSettings.WindowTitle = "Tokis Revenge";
        windowSettings.VSync=true;
        windowSettings.WindowFullscreen=false;
        windowSettings.WindowResizable=true;
        windowSettings.WindowXres=1000;
        windowSettings.WindowYres=600;
		glRenderer = new NGLR(windowSettings);
        glRenderer.LoadProjectionSettings(projectionSettings);
		in = new BasicInput();
		isLevelLoaded = false;
		renderable = new ArrayList<EntityCoordinate>();
		activeEntities = new HashSet<Entity>();
		activeProjectiles = new HashSet<EntityProjectile>();
		Effects = new HashSet<Effect>();
		idModelKills = new int[3];
		idModelStatics = new int[2];
		idTokiCurse = new int[4];
		s = Collections.synchronizedSet(activeProjectiles);
		e = Collections.synchronizedSet(activeEntities);
		staticSpawnRate = 0.0001f;
		lastStatic = 0.0f;
	}
	
	void loadLevel(String levelSelectID)
	{	
		parser = new levelParser("levelMap","res/"+levelSelectID);
		bground = new Background(offset, -1.0f, 1.0f, true, 0.0f, 1.0f, 3, "res/background.png");
		glRenderer.LoadBackground(bground);
		player = new EntityPlayer();
		idModelToki = glRenderer.LoadObject(player.getModelType());
		player.eCoord.modelID = idModelToki;
		SpawnableEntityID = parser.GetSameNameAttributes("enemyTypes", "Type");
		
		if(SpawnableEntityID!=null)
		{
			//System.out.println(SpawnableEntityID[0][0]);
			loadTextures();
		}else{
			System.out.println("No enemies in XML to spawn");
		}
		String[] properties = {"type", "timeOffset"};
		Spawnlist = parser.GetAttributes("enemy", properties);
		if(Spawnlist!=null)
			{
				cycleEnemies();
			}
		else
		{
			System.out.println("spawnlist pulled as empty!");
		}

		player.setpos(0, 0);
		spawnPlayer(player);
		isLevelLoaded = true;
	}
	
	void loadMenu()
	{
		//This will render a menu!
	}
	
	void unloadCurrentLevel()
	{
		Iterator<Entity> killIterator;
		killIterator = activeEntities.iterator();
		while(killIterator.hasNext())
		{
			killIterator.next().destroy();
		}
		isLevelLoaded = false;
		loadMenu();
	}
	
	void loadBackground(int sceneryID)
	{
		//TODO: implement background loading based on id
		//glRenderer.LoadObject(scenery)
	}
	
	void loadTextures()
	{
		EntityEnemy tEnemy;
		for(int i=0; i<SpawnableEntityID.length; i++)
		{
			//System.out.println("GameLogic."+SpawnableEntityID[i][0]);
			if(("GameLogic."+SpawnableEntityID[i]).equals(EnemyDummy.class.getName()))
			{
				System.out.println("Loaded dummy");
				tEnemy = new EnemyDummy();
				glRenderer.LoadObject(tEnemy.getModelType());
			}
			if(("GameLogic."+SpawnableEntityID[i]).equals(EnemyElfArcher.class.getName()))
			{
				System.out.println("Loaded archer");
				tEnemy = new EnemyElfArcher();
				idModelElf = glRenderer.LoadObject(tEnemy.getModelType());
			}
			if(("GameLogic."+SpawnableEntityID[i]).equals(EnemyElfMage.class.getName()))
			{
				System.out.println("Loaded mage");
				tEnemy = new EnemyElfMage();
				idModelMage = glRenderer.LoadObject(tEnemy.getModelType());
			}
			if(("GameLogic."+SpawnableEntityID[i]).equals(EnemyOwlBomb.class.getName()))
			{
				System.out.println("Loaded owlbomb");
				tEnemy = new EnemyOwlBomb();
				idModelOwl = glRenderer.LoadObject(tEnemy.getModelType());
			}
		}
		EntityProjectile pInstance = new ProjectileAxe();
		idModelAxe = glRenderer.LoadObject(pInstance.getModelType());
		pInstance = new ProjectileMagic();
		idModelMagic = glRenderer.LoadObject(pInstance.getModelType());
		EffectDeath dEffect = new EffectDeath();
		dEffect.model.texture_path = "res/toki_kill_1.png";
		idModelKills[0] = glRenderer.LoadObject(dEffect.getModelType());

		dEffect.model.texture_path = "res/toki_kill_2.png";
		idModelKills[1] = glRenderer.LoadObject(dEffect.getModelType());
		
		dEffect.model.texture_path = "res/toki_kill_3.png";
		idModelKills[2] = glRenderer.LoadObject(dEffect.getModelType());
	
		//idModelDeath = glRenderer.LoadObject(dEffect.getModelType());
		EffectSplatter sEffect = new EffectSplatter();
		idModelSplatter = glRenderer.LoadObject(sEffect.getModelType());
	
		EntityStatic templateStatic = new StaticTree();
		idModelStatics[0] = glRenderer.LoadObject(templateStatic.getModelType());
		templateStatic = new StaticCloud();
		idModelStatics[1] = glRenderer.LoadObject(templateStatic.getModelType());
		
		EffectCurse templateCurse = new EffectCurse();
		templateCurse.model.texture_path = "res/tokiCurse1.png";
		idTokiCurse[0] = glRenderer.LoadObject(templateCurse.getModelType());

		templateCurse.model.texture_path = "res/tokiCurse2.png";
		idTokiCurse[1] = glRenderer.LoadObject(templateCurse.getModelType());

		templateCurse.model.texture_path = "res/tokiCurse3.png";
		idTokiCurse[2] = glRenderer.LoadObject(templateCurse.getModelType());
		
		templateCurse.model.texture_path = "res/tokiCurse4.png";
		idTokiCurse[3] = glRenderer.LoadObject(templateCurse.getModelType());
	}
	
	public synchronized void tick(){		
	//simply call for entity update every tick
		Iterator<Entity> entityIterator;
		Iterator<Entity> ugly;
		Iterator<EntityProjectile> projectileIterator;
		Iterator<Effect> dEffects;
		Entity temporaryEntity;
		EntityProjectile thisProjectile;
		a = in.update();
		
		 switch (a){
         case 1 : tmp = -0.01 ; break;

         case 2 : tmp = 0.01; break;

         case 3 : tmp2 = -0.01; break;
         case 4 : tmp2 = 0.01; break;

         //case 5 : glRenderer.LoadProjectionSettings(projectionSettings); break;

         case 11 : tmp = 0; break;
         case 12 : tmp = 0; break;

         case 13 : tmp2 = 0; break;
         case 14 : tmp2 = 0; break;
	
         case 5: player.attack(); spawnProjectile(player.eCoord, "axe");
		 }

		player.move(tmp2, tmp);
		 cycleEnemies();//spawner run
		 spawnStatic(); //static spawner
			//entityIterator = activeEntities.iterator();
			//projectileIterator = activeProjectiles.iterator();
			EntityEnemy eI;
		synchronized(activeEntities)
		{
		 	entityIterator = e.iterator();
			while(entityIterator.hasNext())
			{
			  if(entityIterator.hasNext())
			  {
				temporaryEntity = entityIterator.next();
				if(temporaryEntity != player && temporaryEntity.isInRadiusOf(player, player.collisionRadius) && temporaryEntity.isEnemy && !player.isGrace())
				{
					//System.out.println("Collision occurs, player should be destroyed!");
					DamagePlayer();
				}
				if(temporaryEntity.isEnemy)
				{
					eI = (EntityEnemy)temporaryEntity;
					eI.tick();
					//temporaryEntity.tick();
				}
				else
				{
					temporaryEntity.tick();
				}
				if(temporaryEntity!=player && !(temporaryEntity instanceof EntityStatic) && ((EntityEnemy)temporaryEntity).isAttacking())
				{
					if(!(temporaryEntity instanceof EnemyBoss))
					{
					((EntityEnemy)temporaryEntity).attack();
					if((temporaryEntity.getClass().getName()).equals("GameLogic.EnemyElfMage"))
					{	
						spawnProjectile(temporaryEntity.eCoord, "magic");
					}
					if((temporaryEntity.getClass().getName()).equals("GameLogic.EnemyElfArcher"))
					{	
						spawnProjectile(temporaryEntity.eCoord, "arrow");
					}
					}
					else if(temporaryEntity instanceof EnemyBoss)
					{
						if(((EnemyBoss)temporaryEntity).attackType() == 1)
						{
							spawnProjectile(temporaryEntity.eCoord, "magic");
						}
						if(((EnemyBoss)temporaryEntity).attackType() == 2)
						{
							
								spawnProjectile(temporaryEntity.eCoord, "boss_fireball");
							
						}
					}
				}
				if(temporaryEntity!=player &&temporaryEntity.eCoord.posX < (-2)*glRenderer.GetAspectRatio())
				{
					entityIterator.remove();
					renderable.remove(temporaryEntity.eCoord);
				}
				if(temporaryEntity!=player && Math.abs(temporaryEntity.eCoord.posY) > 1.5f)
				{
					entityIterator.remove();
					renderable.remove(temporaryEntity.eCoord);
				}
				if(temporaryEntity.isEnemy)
				{
					if(((EntityEnemy)temporaryEntity).isDead)
					{	
						player.awardScore(temporaryEntity.value);
						entityIterator.remove();
						renderable.remove(temporaryEntity.eCoord);
					}
				}
			  }
				//drawableEntities.add(entityIterator.next().eCoord);
			  }
		
		synchronized(activeProjectiles)
		{
		 	projectileIterator = s.iterator();
			while(projectileIterator.hasNext())
			{
				ugly = e.iterator();
				if(projectileIterator.hasNext())
				{
					thisProjectile = projectileIterator.next();
					thisProjectile.tick();
				
					if(thisProjectile.eCoord.posX > glRenderer.GetAspectRatio()*1.5f)
						{
							projectileIterator.remove();
							renderable.remove(thisProjectile.eCoord);
							continue;
						}
	
					if(thisProjectile.shouldBeRemoved)
						{
							projectileIterator.remove();
							renderable.remove(thisProjectile.eCoord);
							continue;
						}
					
		
					while(ugly.hasNext())
					{
						if(ugly.hasNext())
						{
							temporaryEntity = ugly.next();
							
							if(temporaryEntity!=player && !(temporaryEntity instanceof EntityStatic))
							{	
								if(!thisProjectile.isEnemy && temporaryEntity.isInRadiusOf((Entity)thisProjectile, thisProjectile.collisionRadius))
								{
									temporaryEntity.subtractHealth(thisProjectile.damageDealt);
									System.out.println(thisProjectile.damageDealt);
									System.out.println("Score: "+player.score);
									EffectDeath death = new EffectDeath(thisProjectile.eCoord);
									death.eCoord.modelID = idModelKills[(int)(Math.random() * 2)];
									spawnEffect(death);
									spawnSplatter(thisProjectile.eCoord);
									if(!thisProjectile.isPenetrating)
									{
										
				
											//System.out.println("Removing "+thisProjectile.getClass());
											thisProjectile.shouldBeRemoved = true;
											/*try{
												
												projectileIterator.remove();
												renderable.remove(thisProjectile.eCoord);
											}catch(Exception e)
											{
												//Ugly hack, but exception in this case IS HARMLESS.
											}*/
									}
									
								}
							}
							else if(thisProjectile.isEnemy)
							{
								if(thisProjectile.isInRadiusOf(player, player.collisionRadius) && !player.isGrace())
								{
									player.damage(thisProjectile.damage);
									DamagePlayer();
								}
							}
						}
					
					}
				}
			}
			}
		}
		synchronized(Effects)
		{
			dEffects = Effects.iterator();
			while(dEffects.hasNext())
			{
				if(dEffects.hasNext())
				{
					Effect tEffect = dEffects.next();
					if(tEffect.hasExpired())
					{
						dEffects.remove();
						renderable.remove(tEffect.eCoord);
					}
					else
					{
						tEffect.tick();
					}
				}
			}
		}
		//deltaTime = deltaTime - System.currentTimeMillis();	
		//offset-=deltaTime*(0.0005);
		//deltaTime = System.currentTimeMillis();
		//System.out.println(offset);
		offset+=0.0041f;
        glRenderer.DrawFrame(renderable, 0.0, 0.0);
	}
	
	public void spawnByName(String entityName)
	{
		if(entityName.equals("EnemyDummy"))
		{
			EnemyDummy nEnemy = new EnemyDummy();
			spawnEnemy(nEnemy);
		}
	}
	
	public synchronized void spawnEntity(Entity a)
	{
		synchronized(activeEntities)
		{
			activeEntities.add(a);
		}
		a.tick(); //a will be updated, possibly replace with initialise
	}
	
	/*public void spawnProjectile(EntityProjectile p)
	{
		p.onscreen = true;
		renderable.add(p.eCoord);
		activeEntities.add(p);
	}*/
	
	public synchronized void spawnEnemy(EntityEnemy a)
	{
		a.setTarget(player);
		a.onscreen = true;
		a.isEnemy = true;
		synchronized(renderable)
		{
			renderable.add(a.eCoord);
		}
		spawnEntity(a);
	}
	
	public synchronized void spawnPlayer(EntityPlayer p)
	{
		p.onscreen = true;
		p.isEnemy = false;
		renderable.add(p.eCoord);
		activeEntities.add(p);
		p.tick();
	}
	
	public synchronized void removeEntity(Entity a)
	{
		synchronized(activeEntities)
		{
			e.remove(a);
		}
		synchronized(renderable)
		{
			renderable.remove(a.eCoord);
		}
	}
	
	public synchronized void spawnEffect(EffectDeath eff)
	{
		synchronized(Effects)
		{
			Effects.add(eff);
		}
		synchronized(renderable)
		{
			renderable.add(eff.eCoord);
		}
	}
	
	public synchronized void spawnSplatter(EntityCoordinate source)
	{
		for(int i=0; i<50; i++)
		{
			EffectSplatter tSplatter = new EffectSplatter(source);
			tSplatter.eCoord.modelID = idModelSplatter;
			synchronized(Effects)
			{
				Effects.add(tSplatter);
			}
			synchronized(renderable)
			{
				renderable.add(tSplatter.eCoord);
			}
		}
	}
	
	public void spawnStatic()
	{
		EntityStatic thisStatic;
		if(Math.random() < lastStatic)
		{
			if(Math.random()-0.5f > 0)
			{
				thisStatic = new StaticTree();
				thisStatic.setpos(glRenderer.GetAspectRatio()*1 , glRenderer.GetAspectRatio()*(-0.8));
				thisStatic.eCoord.modelID = idModelStatics[0];
				synchronized(renderable)
				{
					renderable.add(thisStatic.eCoord);
				}
				spawnEntity(thisStatic);
			}
			else
			{
				thisStatic = new StaticCloud();
				thisStatic.setpos(glRenderer.GetAspectRatio()*1, (glRenderer.GetAspectRatio()*(0.8)-Math.random()/10.0f));
				thisStatic.eCoord.modelID = idModelStatics[1];
				synchronized(renderable)
				{
					renderable.add(thisStatic.eCoord);
				
				}
				spawnEntity(thisStatic);
			}
			lastStatic = 0;
		}
		else
		{
			lastStatic += staticSpawnRate;
		}
	}
	
	public void cycleEnemies()
	{
		EntityEnemy tEnemy;
		if(Spawnlist == null)
		{
			return;
		}
		synchronized(activeEntities)
		{
		for(int i=0; i<Spawnlist.length; i++)
		{
			if(Spawnlist[i]!=null && Spawnlist[i][1]!=null && Double.valueOf(Spawnlist[i][1]) < offset)
			{
				System.out.println("Spawn val: "+Double.valueOf(Spawnlist[i][1])+" offset "+offset);
				if(("GameLogic."+Spawnlist[i][0]).equals(EnemyDummy.class.getName()))
				{
					System.out.println("Loaded dummy");
					tEnemy = new EnemyDummy();
					tEnemy.setpos(1,0);
					spawnEnemy(tEnemy);
					removeFromSpawnable(i);
					continue;
				}
				if(("GameLogic."+Spawnlist[i][0]).equals(EnemyElfArcher.class.getName()))
				{
					System.out.println("Loaded archer");
					tEnemy = new EnemyElfArcher();
					tEnemy.setpos(glRenderer.GetAspectRatio()*1 , glRenderer.GetAspectRatio()*(2*(Math.random())-0.5)*0.8);
					tEnemy.eCoord.modelID = idModelElf;
					spawnEnemy(tEnemy);
					removeFromSpawnable(i);
					continue;
				}
				if(("GameLogic."+Spawnlist[i][0]).equals(EnemyElfMage.class.getName()))
				{
					System.out.println("Loaded mage");
					tEnemy = new EnemyElfMage();
					tEnemy.setpos(glRenderer.GetAspectRatio()*1 , glRenderer.GetAspectRatio()*(2*(Math.random())-0.5)*0.8);
					tEnemy.eCoord.modelID = idModelMage;
					spawnEnemy(tEnemy);
					removeFromSpawnable(i);
					continue;
				}
				if(("GameLogic."+Spawnlist[i][0]).equals(EnemyOwlBomb.class.getName()))
				{
					System.out.println("Loaded owl");
					tEnemy = new EnemyOwlBomb();
					tEnemy.setpos(glRenderer.GetAspectRatio()*1 , glRenderer.GetAspectRatio()*(2*(Math.random())-0.5)*0.8);
					tEnemy.eCoord.modelID = idModelOwl;
					spawnEnemy(tEnemy);
					removeFromSpawnable(i);
					continue;
				}
				if(("GameLogic."+Spawnlist[i][0]).equals(EnemyBoss.class.getName()))
				{
					System.out.println("Loaded boss");
					tEnemy = new EnemyBoss();
					tEnemy.setpos(glRenderer.GetAspectRatio()*1 , glRenderer.GetAspectRatio()*(2*(Math.random())-0.5)*0.8);
					tEnemy.eCoord.modelID = idModelMage;
					spawnEnemy(tEnemy);
					removeFromSpawnable(i);
					continue;
				}
			}
		}
		}
	}
	

	private String[][] removeFromSpawnable(int index)
	{
		List<String[]> trimmedList = new LinkedList<String[]>();
		
		for(int i=0; i<Spawnlist.length; i++)
		{
			if(!(i == index))
			{
				trimmedList.add(Spawnlist[i]);
			}
		}
		
		return trimmedList.toArray(Spawnlist);
	}
	
	private synchronized void spawnProjectile(EntityCoordinate position, String projectileType)
	{
		synchronized(activeProjectiles)
		{
		EntityProjectile projectile = null;
		if(projectileType == "axe")
		{
			projectile = new ProjectileAxe();
			projectile.eCoord.modelID = idModelAxe;
		}
		if(projectileType == "magic")
		{
			projectile = new ProjectileMagic();
			projectile.eCoord.modelID = idModelMagic;
		}
		if(projectileType == "arrow")
		{
			projectile = new ProjectileArrow(player.eCoord);
			projectile.eCoord.modelID = idModelMagic;
		}
		if(projectileType == "boss_fireball")
		{
			
				projectile = new ProjectileBossFireball();
				((ProjectileBossFireball)projectile).setAngle(0);
				projectile.eCoord.modelID = idModelMagic;
			
		}
		if(projectile!=null)
		{
		projectile.setpos(position.posX, position.posY);
		renderable.add(projectile.eCoord);
		activeProjectiles.add(projectile);
		//System.out.println("Spawning projectile "+projectile.getClass());
		//projectile.tick();
		}
		}
	}
	
	private void DamagePlayer()
	{
		EffectCurse thisCurse = new EffectCurse(player.eCoord);
		thisCurse.eCoord.modelID = idTokiCurse[(int)Math.random()*4];
		
		synchronized(Effects)
		{
			Effects.add(thisCurse);
		}
		synchronized(renderable)
		{
			renderable.add(thisCurse.eCoord);
		}
		
		player.activateGrace();
	}
}
